use crate::ast::*;
use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::values::BasicValueEnum;

impl<'ctx> LLVMBackend<'ctx> {


    /// Generate unwrap() method - returns Ok value or panics with Err value
    pub(in crate::llvm_backend) fn generate_result_unwrap(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "unwrap_check")?;

        // Create blocks: ok_block (return value), err_block (panic)
        let ok_block = self.context.append_basic_block(func, "unwrap_ok");
        let err_block = self.context.append_basic_block(func, "unwrap_err");
        let continue_block = self.context.append_basic_block(func, "unwrap_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: extract and return the value
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: extract error value and panic
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;

        // Store err_value in a temporary variable so we can use it in panic
        let err_type = self.nrc_type_to_llvm_type(&Some(*result_type.err_type.clone()))?;
        let err_temp_alloca = self.builder.build_alloca(err_type, "err_temp")?;
        self.builder.build_store(err_temp_alloca, err_value)?;

        // Create a temporary variable name for the error value
        let err_temp_var = format!("__unwrap_err_{}", location.line);

        // Store the alloca in variable_map temporarily
        let old_err_value = self
            .variable_map
            .insert(err_temp_var.clone(), err_temp_alloca.into());
        let old_err_type = self
            .variable_type_map
            .insert(err_temp_var.clone(), *result_type.err_type.clone());

        // Create a panic expression with the error value
        let err_var_expr = Expression::Variable(err_temp_var.clone());
        let panic_expr = PanicExpr {
            value: Box::new(err_var_expr),
            location: *location,
        };

        // Generate panic with error value
        let _ = self.generate_panic_expression(&panic_expr)?;

        // Restore variable map (if there was a previous value)
        if let Some(old) = old_err_value {
            self.variable_map.insert(err_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&err_temp_var);
        }
        if let Some(old) = old_err_type {
            self.variable_type_map.insert(err_temp_var, old);
        } else {
            self.variable_type_map.remove(&err_temp_var);
        }

        // Continue block: return the Ok value
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.ok_type.clone()))?,
            "unwrap_result",
        )?;
        phi.add_incoming(&[(&ok_value, ok_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate unwrap_err() method - returns Err value or panics if Ok
    pub(in crate::llvm_backend) fn generate_result_unwrap_err(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let one = self.context.i32_type().const_int(1, false);
        let is_err = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            one,
            "unwrap_err_check",
        )?;

        // Create blocks: err_block (return value), ok_block (panic)
        let err_block = self.context.append_basic_block(func, "unwrap_err_err");
        let ok_block = self.context.append_basic_block(func, "unwrap_err_ok");
        let continue_block = self.context.append_basic_block(func, "unwrap_err_continue");

        self.builder
            .build_conditional_branch(is_err, err_block, ok_block)?;

        // Err branch: extract and return the error value
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Ok branch: panic
        // For unwrap_err(), we panic with a message since we don't have a meaningful error value
        // In this case, we panic with a string message
        self.builder.position_at_end(ok_block);
        let _ = self.generate_panic_expression(&PanicExpr {
            value: Box::new(Expression::Literal(Literal::String(
                "called `Result::unwrap_err()` on an `Ok` value".to_string(),
            ))),
            location: *location,
        })?;

        // Continue block: return the Err value
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.err_type.clone()))?,
            "unwrap_err_result",
        )?;
        phi.add_incoming(&[(&err_value, err_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate expect() method - returns Ok value or panics with message
    pub(in crate::llvm_backend) fn generate_result_expect(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.expect() expects 1 argument (message), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "expect_check")?;

        // Create blocks: ok_block (return value), err_block (panic with message)
        let ok_block = self.context.append_basic_block(func, "expect_ok");
        let err_block = self.context.append_basic_block(func, "expect_err");
        let continue_block = self.context.append_basic_block(func, "expect_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: extract and return the value
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: panic with message
        self.builder.position_at_end(err_block);
        let panic_expr = PanicExpr {
            value: Box::new(arguments[0].clone()),
            location: *location,
        };
        let _ = self.generate_panic_expression(&panic_expr)?;

        // Continue block: return the Ok value
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.ok_type.clone()))?,
            "expect_result",
        )?;
        phi.add_incoming(&[(&ok_value, ok_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate expect_err() method - returns Err value or panics with message
    pub(super) fn generate_result_expect_err(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.expect_err() expects 1 argument (message), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let one = self.context.i32_type().const_int(1, false);
        let is_err = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            one,
            "expect_err_check",
        )?;

        // Create blocks: err_block (return value), ok_block (panic with message)
        let err_block = self.context.append_basic_block(func, "expect_err_err");
        let ok_block = self.context.append_basic_block(func, "expect_err_ok");
        let continue_block = self.context.append_basic_block(func, "expect_err_continue");

        self.builder
            .build_conditional_branch(is_err, err_block, ok_block)?;

        // Err branch: extract and return the error value
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Ok branch: panic with message
        self.builder.position_at_end(ok_block);
        let panic_expr = PanicExpr {
            value: Box::new(arguments[0].clone()),
            location: *location,
        };
        let _ = self.generate_panic_expression(&panic_expr)?;

        // Continue block: return the Err value
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.err_type.clone()))?,
            "expect_err_result",
        )?;
        phi.add_incoming(&[(&err_value, err_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate unwrap_or() method - returns Ok value or default value
    pub(super) fn generate_result_unwrap_or(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.unwrap_or() expects 1 argument (default value), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "unwrap_or_check",
        )?;

        // Create blocks: ok_block (return Ok value), err_block (return default value)
        let ok_block = self.context.append_basic_block(func, "unwrap_or_ok");
        let err_block = self.context.append_basic_block(func, "unwrap_or_err");
        let continue_block = self.context.append_basic_block(func, "unwrap_or_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: extract and return the value
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: return default value
        self.builder.position_at_end(err_block);
        let default_value = self.generate_expression(&arguments[0])?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either Ok value or default
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.ok_type.clone()))?,
            "unwrap_or_result",
        )?;
        phi.add_incoming(&[(&ok_value, ok_block), (&default_value, err_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate unwrap_or_else() method - returns Ok value or calls function with error value
    pub(super) fn generate_result_unwrap_or_else(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.unwrap_or_else() expects 1 argument (function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "unwrap_or_else_check",
        )?;

        // Create blocks: ok_block (return Ok value), err_block (call function)
        let ok_block = self.context.append_basic_block(func, "unwrap_or_else_ok");
        let err_block = self.context.append_basic_block(func, "unwrap_or_else_err");
        let continue_block = self
            .context
            .append_basic_block(func, "unwrap_or_else_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: extract and return the value
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: extract error value and call function
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;

        // Store err_value in a temporary variable so we can use it in function call
        let err_type = self.nrc_type_to_llvm_type(&Some(*result_type.err_type.clone()))?;
        let err_temp_alloca = self.builder.build_alloca(err_type, "err_temp")?;
        self.builder.build_store(err_temp_alloca, err_value)?;

        // Create a temporary variable name for the error value
        let err_temp_var = format!("__unwrap_or_else_err_{}", location.line);

        // Store the alloca in variable_map temporarily
        let old_err_value = self
            .variable_map
            .insert(err_temp_var.clone(), err_temp_alloca.into());
        let old_err_type = self
            .variable_type_map
            .insert(err_temp_var.clone(), *result_type.err_type.clone());

        // Create a call expression: f(err_temp_var)
        let func_expr = arguments[0].clone();
        let err_var_expr = Expression::Variable(err_temp_var.clone());
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(func_expr),
            arguments: vec![err_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });

        // Generate the function call
        let computed_value = self.generate_expression(&call_expr)?;

        // Restore variable map (if there was a previous value)
        if let Some(old) = old_err_value {
            self.variable_map.insert(err_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&err_temp_var);
        }
        if let Some(old) = old_err_type {
            self.variable_type_map.insert(err_temp_var, old);
        } else {
            self.variable_type_map.remove(&err_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either Ok value or computed value
        self.builder.position_at_end(continue_block);
        let phi = self.builder.build_phi(
            self.nrc_type_to_llvm_type(&Some(*result_type.ok_type.clone()))?,
            "unwrap_or_else_result",
        )?;
        phi.add_incoming(&[(&ok_value, ok_block), (&computed_value, err_block)]);
        Ok(phi.as_basic_value())
    }

}
